'''下载分段视频的脚本工具,目前针对的是https://www.zeyi.cc
  创建主类时指定下载首地址url,headers,存档路径filePath,存档文件名fileName.
  _getPartUrl()指定一个部分的下载地址,
  _downAPart()下载一个部分,成功返回True,当返回False时表示下载失败,认为是所有
  部分下载成功.在main()方法中将进入写入文件环节.
  main()方法就是执行下载.
  
  其中下载首地址,请自行打开视频页面,按f12,把视频传输的地址形如:
  https://youku.letv-cdn.com/2019/07/14/wEspUXn9lGL3SiPa/out003.ts
  后面的/out003.ts去掉.
  
  运行时的具体设定请在 ## params后进行
'''

import sys
import os

import requests

class PartVedioDownloader:

  ## 最大请求次数
  MAX_PART = 1000

  def __init__(self, url, headers = {}, filePath = sys.path[0], 
    fileName = 'out.mp4'):
    self.url = url
    self.headers = headers
    self.filePath = filePath
    self.fileName = fileName
    ## 会话
    self.session = requests.Session()
    ## 下载内容保存
    self.content = b''
    return
    
  ## 给出下载第几部分,返回该部分的下载链接
  def _getPartUrl(self, partNum):
    nString = str(partNum)
    partName = 'out'+'0'*(3-len(nString))+nString+'.ts'
    return self.url+'/'+partName
    
  ## 下载第partNum部分,失败返回False,一般能作为下载完成的指示
  def _downAPart(self, partNum):
    url = self._getPartUrl(partNum)
    resp = self.session.get(url, headers = headers)
    if resp.status_code != 200:
      return False
    else:
      self.content += resp.content
    return True
    
  ## 主函数,执行下载以及写入内容到下载文件
  def main(self):
    ## 下载的目标文件
    fileFullPath = self.filePath+'\\'+self.fileName
    while os.path.exists(fileFullPath):
      fileFullPath = fileFullPath+'.new'
    try:
      file = open(fileFullPath, 'wb')
      print('\n---\n下载文件创建成功:\n{0}\n'.format(fileFullPath))
    except:
      print('目标文件无法创建!')
      return
    ## 开始下载
    print('\n---\n开始发送下载请求:')
    for i in range(0, self.MAX_PART):
      mb = len(self.content)/1048576.0
      msg = ('已下载 {0:.2f} MB'.format(mb))
      sys.stdout.write('\r{0}'.format(msg))
      sys.stdout.flush()
      # 遇到下载失败的部分即结束
      if not self._downAPart(i):
        print('下载完成!')
        break
    ## 写入文件
    if len(self.content) > 0:    
      print('\n---\n开始写入文件')    
      file.write(self.content)
      print('写入文件结束!')
    else:
      print('\n---\n下载字节数为0,可能下载失败,不能写入文件')
    file.close()
    ## 完成
    return
    
    
## Main script:
if __name__ == '__main__':
  ## params
  url = r'https://youku.letv-cdn.com/2019/07/07/PV1X12LuSLsSoh8P'
  headers = {
    'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Accept': '*/*',
    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'Accept-Encoding': 'gzip, deflate, br',
    'Connection': 'keep-alive'
  }
  filePath = sys.path[0]+r'\擅长捉弄的高木同学_第二季'
  fileName = '01.mp4'
  ## starts
  x = input('为避免失误开始,请输入1+回车开始脚本')
  if x != '1':
    print('输入并非开始指令,脚本结束!')
    exit(0)
  pv = PartVedioDownloader(url = url, 
    headers = headers, 
    filePath = filePath, 
    fileName = fileName)
  pv.main()
  exit(0)